#!/usr/bin/env python

##  flamingo - 2D Game Engine
##  Copyright (C) 2009 Bradley Zeis
##
##  This program is free software: you can redistribute it and/or modify
##  it under the terms of the GNU General Public License as published by
##  the Free Software Foundation, either version 3 of the License, or
##  (at your option) any later version.
##
##  This program is distributed in the hope that it will be useful,
##  but WITHOUT ANY WARRANTY; without even the implied warranty of
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##  GNU General Public License for more details.
##
##  You should have received a copy of the GNU General Public License
##  along with this program.  If not, see <http://www.gnu.org/licenses/>.
##
##  Bradley Zeis
##  flamingoengine@gmail.com

"""Contains useful utility functions for use throughout Flamingo.

General Functions:
    create_key(dict)            - Return a 7-digit number that is not a
                                  key in dict.
    to_filename(name)           - Return name as an appropriate filename.
    hasattrs(object, *names)    - Return True if object has all attributes
                                  names.
    format_types(type)          - Return the names of types without the
                                  extra formatting.

    enable_logging()            - Enable global exception logging.
    disable_logging()           - Disable exception logging.
"""

import sys, os
import random
import re
import logging

##-------- Constants
ERRORLOG = "flamingo-error.txt"

##-------- General Functions
def create_key(dict=None):
    """Return a 7-digit key that is not a key in dict.

    dict        - (dict) If provided, a key in dict won't be returned.

    Returns: int
    """
    def key():
        random.seed()
        return int(random.random() * 10000000)

    if dict:
        k = key()
        while k in dict: k = key()
        return k

    return key()

def to_filename(name):
    """Return the name as an appropriate filename without the extension.

    This function is mainly used by the Flamingo Tools.

    name        - (str) The name of the segment.

    Returns: str
    """
    segname = segname.lower()
    string = ""
    for i in range(0, len(segname)):
        if segname[i] == " ": string += "_"
        elif re.search("[a-z0-9_-]|\.", segname[i]): string += segname[i]
    return string

def hasattrs(object, *names):
    """Return True if object has all attributes names.

    Returns: bool
    """
    return all(hasattr(object, name) for name in names)

def format_types(*types):
    """Return the names of types without the extra formatting.

    Usage:

        >>> format_types(tuple, int, float)
        'tuple, int float'

    Returns: str
    """
    return ", ".join([str(t).split("'")[1] for t in types])

##-------- Exception Logging
def enable_logging():
    """Enable global exception logging.

    Returns: None
    """
    if os.path.abspath(os.curdir) != sys.prefix:
        sys.excepthook = _excepthook

def disable_logging():
    """Disable exception logging.

    Returns: None
    """
    sys.excepthook = sys.__excepthook__

def _excepthook(type, value, tback):
    """Append the traceback to the end of the global error log."""
    with open(ERRORLOG, "a") as log:
        import traceback
        tb = traceback.format_exception(type, value, tback)
        for line in tb:
            log.write(line)
            print line
        log.write("\n\n")

##-------- Decorators
## Memoization
class memoize(object):

    def __init__(self, func):
        self.cache = {}
        self.func = func

    def __call__(self, *args, **kw):
        try:
            return self.cache[(args, kw)]
        except KeyError:
            self.cache[(args, kw)] = value = self.func(*args, **kw)
            return value
        except TypeError:
            return self.func(*args, **kw)

    def __repr__(self):
        return self.func.__doc__
